package ms.unicorn.dsl;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ms.unicorn.Diagram;
import ms.unicorn.Element;
import ms.unicorn.IPropertyDescriptorsProvider;
import ms.unicorn.dsl.analysis.IConversionScheme;
import ms.unicorn.dsl.analysis.UnicornService;
import ms.unicorn.dsl.impl.ModelLanguage;
import ms.unicorn.dsl.unicornDSL.UnicornConversion;
import ms.unicorn.util.PropertyDescriptorsProvider;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.ResourceLocator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.edit.provider.ComposeableAdapterFactory;
import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
import org.eclipse.gmf.runtime.emf.type.core.ElementTypeRegistry;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.IMetamodelType;
import org.eclipse.gmf.runtime.emf.type.core.ISpecializationType;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.xtext.diagnostics.AbstractDiagnostic;

public class LanguageRegistry implements IPropertyDescriptorsProvider {

	
	private static final String LANGUAGE_DEFINITION_FILEEXT = ".dsl";
	public static final LanguageRegistry INSTANCE = new LanguageRegistry();
	private List<IModelLanguage> languages;
	private static Map<String, ILanguageElement> elementMap;
	ResourceSet rs = new ResourceSetImpl();

	private LanguageRegistry() {
		languages = new ArrayList<IModelLanguage>();
		elementMap = new HashMap<String, ILanguageElement>();
		UnicornDSLStandaloneSetup.doSetup();
		initialize();
		PropertyDescriptorsProvider.INSTANCE = this; 
	}
	
	protected void initialize() {
		File langDir = new File("languages");
		File[] files = langDir.listFiles(new FileFilter() {

			@Override
			public boolean accept(File file) {
				return file.getName().toLowerCase().endsWith(LANGUAGE_DEFINITION_FILEEXT);
			}
			
		});
		for (File file: files) 
		try {
			Resource resource = rs.getResource(URI.createFileURI(file.getAbsolutePath()), true);
			EList<Diagnostic> errors = resource.getErrors();
			
			if (errors.size() > 0) {
				for(Diagnostic err: errors)  {
					if (err instanceof AbstractDiagnostic) {
						AbstractDiagnostic e = (AbstractDiagnostic) err;
						//INode node = ((AbstractDiagnostic) err).getNode();
						//URI uriToProblem = e.getUriToProblem();
						System.out.printf("Syntax Error [line:%d]: %s\n", e.getLine(), err.getMessage());
					}
				}
			}
			else {
				EObject eobj = resource.getContents().get(0);
				if (eobj instanceof ModelLanguage) {
					ModelLanguage lang = (ModelLanguage)eobj;
					System.out.printf("Register language: %s\n", lang.getText());
					languages.add((ModelLanguage) eobj);
					
					//populate language elements and put them to the elementMap
					for (ILanguageElement e: lang.getLanguageElements()) {
						elementMap.put(e.getId(), e);
						IElementType type = e.getIElementType();
						if (type instanceof IMetamodelType) 
							ElementTypeRegistry.getInstance().register((IMetamodelType) type);
						else if (type instanceof ISpecializationType)
							ElementTypeRegistry.getInstance().register((ISpecializationType)type);
						
						// register phantom attribute
						//e.registerPhantomAttributes();
					}
				}
				else if (eobj instanceof UnicornConversion) {
					// register conversion scheme
					for (EObject obj: ((UnicornConversion) eobj).getElements())
						if (obj instanceof IConversionScheme) {
							System.out.printf("Register conversion scheme: %s\n", ((IConversionScheme) obj).getName());
							UnicornService.getConversionService().registerConversionScheme((IConversionScheme) obj);
						}
				}
			}
		}
		catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	public List<IModelLanguage> getLanguages(boolean reinit) {
		return languages;
	}

	public IModelLanguage getLanguage(String name) {
		for(IModelLanguage language: getLanguages(false))
			if (language.getName().equals(name)) return language;
		return null;
	}

	public ILanguageElement getLanguageElement(Element domainElement) {
		ILanguageElement e = null;
		if (domainElement != null) {
			String typeId = domainElement.getElementType();
			e = elementMap.get(typeId);
			if (e == null && domainElement instanceof Diagram) { 
				IModelLanguage language = getLanguage(domainElement.getLanguage());
				if (language != null)
					e = language.getDiagram(typeId);
				else 
					System.out.println("DEBUG: language is null;")
					;
			}
		}
		return e;
	}
	
	public ILanguageElement getLanguageElement(View view) {
		if (view != null && view.getElement() instanceof Element)
			return getLanguageElement((Element)view.getElement());
		return null;
	}

	@Override
	public List<IItemPropertyDescriptor> getPropertyDescriptors(
			ComposeableAdapterFactory composeableAdapterFactory,
			ResourceLocator resourceLocator, Element object,
			List<IItemPropertyDescriptor> list) {
		ILanguageElement e = getLanguageElement(object);
		if (e != null)
			return e.getPropertyDescriptors(composeableAdapterFactory, resourceLocator, list);
		return list;
	}

	public ILanguageElement getLanguageElement(String typeId) {
		ILanguageElement e = null;
		e = elementMap.get(typeId);
		return e;
	}

}
